/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.features.registry;

import org.picocontainer.ComponentAdapter;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.Parameter;
import org.picocontainer.PicoContainer;
import org.picocontainer.PicoException;
import org.picocontainer.PicoRegistrationException;
import org.picocontainer.PicoVerificationException;
import org.picocontainer.PicoVisitor;
import org.picocontainer.defaults.DuplicateComponentKeyRegistrationException;
import org.picocontainer.defaults.NotConcreteRegistrationException;

import picounit.util.Equal;
import picounit.verify.constraint.EqualUtil;

import java.util.Collection;
import java.util.List;

public class SilentlyReplacingMutablePicoContainer
	implements MutablePicoContainer, Equal<SilentlyReplacingMutablePicoContainer> {

	private final MutablePicoContainer delegate;

	public static MutablePicoContainer wrap(MutablePicoContainer picoContainer) {
		return picoContainer instanceof SilentlyReplacingMutablePicoContainer
			? picoContainer
			: new SilentlyReplacingMutablePicoContainer(picoContainer);
	}

	public SilentlyReplacingMutablePicoContainer(MutablePicoContainer delegate) {
		this.delegate = delegate;
	}
	
	public boolean delegatesTo(MutablePicoContainer delegate) {
		return this.delegate.equals(delegate);
	}

	///////////////////////////////////////////////////////////////////////////////////////////////
	// Equal methods
	///////////////////////////////////////////////////////////////////////////////////////////////
	
	public boolean equalTo(SilentlyReplacingMutablePicoContainer silentlyReplacingMutablePicoContainer) {
		return silentlyReplacingMutablePicoContainer.delegatesTo(delegate);
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// MutablePicoContainer methods
	///////////////////////////////////////////////////////////////////////////////////////////////

	public ComponentAdapter registerComponentImplementation(Object componentKey,
		Class componentImplementation) throws PicoRegistrationException {

		try {
			return delegate.registerComponentImplementation(componentKey, componentImplementation);
		}
		catch (DuplicateComponentKeyRegistrationException duplicateComponentKeyRegistrationException) {
			delegate.unregisterComponent(componentKey);
			return delegate.registerComponentImplementation(componentKey, componentImplementation);
		}
	}

	public ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation, Parameter[] parameters) throws PicoRegistrationException {
		try {
			return delegate.registerComponentImplementation(componentKey, componentImplementation, parameters);
		}
		catch (DuplicateComponentKeyRegistrationException duplicateComponentKeyRegistrationException) {
			delegate.unregisterComponent(componentKey);
			return delegate.registerComponentImplementation(componentKey, componentImplementation, parameters);
		}
	}

	public ComponentAdapter registerComponentImplementation(Class componentImplementation) throws PicoRegistrationException {
		try {
			return delegate.registerComponentImplementation(componentImplementation);
		}
		catch (DuplicateComponentKeyRegistrationException duplicateComponentKeyRegistrationException) {
			delegate.unregisterComponent(componentImplementation);
			return delegate.registerComponentImplementation(componentImplementation);
		}
		catch (NotConcreteRegistrationException notConcreteRegistrationException) {
			throw notConcreteRegistrationException;
		}
	}

	public ComponentAdapter registerComponentInstance(Object componentInstance) throws PicoRegistrationException {
		try {
			return delegate.registerComponentInstance(componentInstance);
		}
		catch (DuplicateComponentKeyRegistrationException duplicateComponentKeyRegistrationException) {
			delegate.unregisterComponent(componentInstance);
			return delegate.registerComponentInstance(componentInstance);
		}
	}

	public ComponentAdapter registerComponentInstance(Object componentKey, Object componentInstance) throws PicoRegistrationException {
		try {
			return delegate.registerComponentInstance(componentKey, componentInstance);
		}
		catch (DuplicateComponentKeyRegistrationException duplicateComponentKeyRegistrationException) {
			delegate.unregisterComponent(componentKey);
			return delegate.registerComponentInstance(componentKey, componentInstance);
		}
	}

	public ComponentAdapter registerComponent(ComponentAdapter componentAdapter) throws PicoRegistrationException {
		try {
			return delegate.registerComponent(componentAdapter);
		}
		catch (DuplicateComponentKeyRegistrationException duplicateComponentKeyRegistrationException) {
			delegate.unregisterComponent(componentAdapter.getComponentKey());
			return delegate.registerComponent(componentAdapter);
		}
	}

	public ComponentAdapter unregisterComponent(Object componentKey) {
		return delegate.unregisterComponent(componentKey);
	}

	public ComponentAdapter unregisterComponentByInstance(Object componentInstance) {
		return delegate.unregisterComponentByInstance(componentInstance);
	}

	public MutablePicoContainer makeChildContainer() {
		return delegate.makeChildContainer();
	}

	public boolean addChildContainer(PicoContainer child) {
		return delegate.addChildContainer(child);
	}

	public boolean removeChildContainer(PicoContainer child) {
		return delegate.removeChildContainer(child);
	}

	public Object getComponentInstance(Object componentKey) {
		return delegate.getComponentInstance(componentKey);
	}

	public Object getComponentInstanceOfType(Class componentType) {
		return delegate.getComponentInstanceOfType(componentType);
	}

	public List getComponentInstances() {
		return delegate.getComponentInstances();
	}

	public PicoContainer getParent() {
		return delegate.getParent();
	}

	public ComponentAdapter getComponentAdapter(Object componentKey) {
		return delegate.getComponentAdapter(componentKey);
	}

	public ComponentAdapter getComponentAdapterOfType(Class componentType) {
		return delegate.getComponentAdapterOfType(componentType);
	}

	public Collection getComponentAdapters() {
		return delegate.getComponentAdapters();
	}

	public List getComponentAdaptersOfType(Class componentType) {
		return delegate.getComponentAdaptersOfType(componentType);
	}

	@SuppressWarnings("deprecation")
	public void verify() throws PicoVerificationException {
		delegate.verify();
	}

	public List getComponentInstancesOfType(Class componentType) throws PicoException {
		return delegate.getComponentInstancesOfType(componentType);
	}

	public void accept(PicoVisitor visitor) {
		delegate.accept(visitor);
	}

	public void start() {
		delegate.start();
	}

	public void stop() {
		delegate.stop();
	}

	public void dispose() {
		delegate.dispose();
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////
	// Object methods
	///////////////////////////////////////////////////////////////////////////////////////////////
	
	@Override
	public boolean equals(Object object) {
		return EqualUtil.isEqual(this, object);
	}

	@Override
	public int hashCode() {
		return getClass().hashCode();
	}
}
